package com.gary.vueblog.shiro;

import cn.hutool.json.JSONUtil;
import com.gary.vueblog.entity.ResultInfo;
import com.gary.vueblog.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.SneakyThrows;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author pengrui
 * @date 2020/12/8 - 14:03
 */
@Component
public class JwtFilter extends AuthenticatingFilter {

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {

        //获取请求头
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        //从请求头中获取token信息
        String jwt = request.getHeader("Authorization");
        //若为空则返回null
        if (!StringUtils.hasText(jwt)) {
            return true;
        }

        //校验jwt
        Claims claimByToken = jwtUtil.getClaimByToken(jwt);
        if (claimByToken == null || jwtUtil.isTokenExpired(claimByToken.getExpiration())) {
            throw new ExpiredCredentialsException("token已失效，请重新登录");
        }
        //执行登录
        return executeLogin(servletRequest, servletResponse);


    }

    @Override
    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        //获取请求头
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        //从请求头中获取token信息
        String jwt = request.getHeader("Authorization");
        //若为空则返回null
        if (jwt.isEmpty()) {
            return null;
        }
        //不为空则创建一个token
        return new JwtToken(jwt);
    }

    /**
     *  当发生异常时 触发此方法
     * @param token
     * @param e
     * @param request
     * @param response
     * @return
     */
    @SneakyThrows
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        // 判断异常cause是否
        Throwable throwable = e.getCause() == null ? e : e.getCause();

        ResultInfo result = ResultInfo.res(500, throwable.getMessage(), null);

        String jsonStr = JSONUtil.toJsonStr(result);

        httpServletResponse.getWriter().print(jsonStr);

        return false;
    }

    /**
     *  预处理 解决跨域问题
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest req = WebUtils.toHttp(request);
        HttpServletResponse resp = WebUtils.toHttp(response);
        resp.setHeader("Access-control-Allow-Origin", req.getHeader("Origin"));
        resp.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
        resp.setHeader("Access-Control-Allow-Headers", req.getHeader("Access-Control-Request-Headers"));
        //跨域时 首先会发送OPTIONS请求，这里我们给OPTIONS请求直接返回正常状态
        if (req.getMethod().equals(RequestMethod.OPTIONS.name())) {
            resp.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }
}
